package main

import (
	"context"
	"crypto/rand"
	"encoding/json"
	"fmt"
	"github.com/olivere/elastic/v7"
	"math/big"
	"time"
)

type logEntry struct {
	Timestamp     time.Time `json:"@timestamp"`
	RemoteAddr    string    `json:"remote_addr"`
	Request       string    `json:"request"`
	Status        int       `json:"status"`
	BodyBytes     int       `json:"body_bytes_sent"`
	HTTPReferer   string    `json:"http_referer"`
	HTTPUserAgent string    `json:"http_user_agent"`
}

// GenerateLog 在本地生成nginx访问日志
func GenerateLog(num int) {
	es, err := EsConnect()
	if err != nil {
		log.Fatal(err)
	}
	log.Info("生成测试日志...")
	for i := 0; i < num; i++ {
		indexLogEntry(es, generateLogEntry(), i)
	}
}

func indexLogEntry(es *elastic.Client, entry interface{}, i int) {
	data, err := json.Marshal(entry)
	if err != nil {
		log.Fatalf("Failed to marshal log entry: %v", err)
	}

	res, err := es.Index().
		Index("nginx-logs").
		BodyString(string(data)).
		Do(context.Background())
	if err != nil {
		log.Fatalf("Failed to index document ID=%d: %v", i+1, err)
	}

	if res.Result == "error" {
		log.Info("Error indexing document ID=%d", i+1)
	} else {
		var r map[string]interface{}
		getRes, err := es.Get().
			Index("nginx-logs").
			Id(res.Id).
			Do(context.Background())
		if err != nil {
			log.Info("Error getting indexed document: %s", err)
		} else if getRes.Found {
			if err := json.Unmarshal(getRes.Source, &r); err != nil {
				log.Info("Error parsing the response body: %s", err)
			}
		}
	}
}

func generateLogEntry() logEntry {
	return logEntry{
		Timestamp:     time.Now(),
		RemoteAddr:    randomIP(),
		Request:       randomRequest(),
		Status:        randomStatus(),
		BodyBytes:     randomBodyBytes(),
		HTTPReferer:   randomReferer(),
		HTTPUserAgent: randomUserAgent(),
	}
}

func randomIP() string {
	n, _ := rand.Int(rand.Reader, big.NewInt(255))
	return fmt.Sprintf("%d.%d.%d.%d", n.Int64(), n.Int64(), n.Int64(), n.Int64())
}

func randomRequest() string {
	methods := []string{"GET", "POST", "PUT", "DELETE"}
	paths := []string{"/", "/index.html", "/about.html", "/contact.html"}
	return fmt.Sprintf("%s %s HTTP/1.1", methods[randomInt(len(methods))], paths[randomInt(len(paths))])
}

func randomStatus() int {
	statuses := []int{200, 201, 204, 400, 401, 403, 404, 500}
	return statuses[randomInt(len(statuses))]
}

func randomBodyBytes() int {
	return randomInt(10000)
}

func randomReferer() string {
	referers := []string{"-", "https://www.google.com/", "https://www.bing.com/", "https://www.yahoo.com/"}
	return referers[randomInt(len(referers))]
}

func randomUserAgent() string {
	agents := []string{
		"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
		"Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; AS; rv:11.0) like Gecko",
		"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_2) AppleWebKit/601.3.9 (KHTML, like Gecko) Version/9.0.2 Safari/601.3.9",
		"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0.1",
		"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Edge/17.17134",
	}
	return agents[randomInt(len(agents))]
}

func randomInt(max int) int {
	n, _ := rand.Int(rand.Reader, big.NewInt(int64(max)))
	return int(n.Int64())
}
